/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* vim: set ts=8 sts=2 et sw=2 tw=80: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */// We're dividing JS objects into 3 categories://// 1. "real" roots, held by the JS engine itself or rooted through the root// and lock JS APIs. Roots from this category are considered black in the// cycle collector, any cycle they participate in is uncollectable.//// 2. certain roots held by C++ objects that are guaranteed to be alive.// Roots from this category are considered black in the cycle collector,// and any cycle they participate in is uncollectable. These roots are// traced from TraceNativeBlackRoots.//// 3. all other roots held by C++ objects that participate in cycle// collection, held by us (see TraceNativeGrayRoots). Roots from this// category are considered grey in the cycle collector; whether or not// they are collected depends on the objects that hold them.//// Note that if a root is in multiple categories the fact that it is in// category 1 or 2 that takes precedence, so it will be considered black.//// During garbage collection we switch to an additional mark color (gray)// when tracing inside TraceNativeGrayRoots. This allows us to walk those// roots later on and add all objects reachable only from them to the// cycle collector.//// Phases://// 1. marking of the roots in category 1 by having the JS GC do its marking// 2. marking of the roots in category 2 by having the JS GC call us back// (via JS_SetExtraGCRootsTracer) and running TraceNativeBlackRoots// 3. marking of the roots in category 3 by TraceNativeGrayRoots using an// additional color (gray).// 4. end of GC, GC can sweep its heap//// At some later point, when the cycle collector runs://// 5. walk gray objects and add them to the cycle collector, cycle collect//// JS objects that are part of cycles the cycle collector breaks will be// collected by the next JS GC.//// If WantAllTraces() is false the cycle collector will not traverse roots// from category 1 or any JS objects held by them. Any JS objects they hold// will already be marked by the JS GC and will thus be colored black// themselves. Any C++ objects they hold will have a missing (untraversed)// edge from the JS object to the C++ object and so it will be marked black// too. This decreases the number of objects that the cycle collector has to// deal with.// To improve debugging, if WantAllTraces() is true all JS objects are// traversed.#include"mozilla/CycleCollectedJSRuntime.h"#include<algorithm>#include"mozilla/ArrayUtils.h"#include"mozilla/AutoRestore.h"#include"mozilla/CycleCollectedJSContext.h"#include"mozilla/Move.h"#include"mozilla/MemoryReporting.h"#include"mozilla/Sprintf.h"#include"mozilla/Telemetry.h"#include"mozilla/TimelineConsumers.h"#include"mozilla/TimelineMarker.h"#include"mozilla/Unused.h"#include"mozilla/DebuggerOnGCRunnable.h"#include"mozilla/dom/DOMJSClass.h"#include"mozilla/dom/ProfileTimelineMarkerBinding.h"#include"mozilla/dom/Promise.h"#include"mozilla/dom/PromiseBinding.h"#include"mozilla/dom/PromiseDebugging.h"#include"mozilla/dom/ScriptSettings.h"#include"jsprf.h"#include"js/Debug.h"#include"js/GCAPI.h"#include"nsContentUtils.h"#include"nsCycleCollectionNoteRootCallback.h"#include"nsCycleCollectionParticipant.h"#include"nsCycleCollector.h"#include"nsDOMJSUtils.h"#include"nsJSUtils.h"#include"nsWrapperCache.h"#include"nsStringBuffer.h"#include"GeckoProfiler.h"#ifdef MOZ_GECKO_PROFILER#include"ProfilerMarkerPayload.h"#endif#ifdef MOZ_CRASHREPORTER#include"nsExceptionHandler.h"#endif#include"nsIException.h"#include"nsIPlatformInfo.h"#include"nsThread.h"#include"nsThreadUtils.h"#include"xpcpublic.h"usingnamespacemozilla;usingnamespacemozilla::dom;namespacemozilla{structDeferredFinalizeFunctionHolder{DeferredFinalizeFunctionrun;void*data;};classIncrementalFinalizeRunnable:publicRunnable{typedefAutoTArray<DeferredFinalizeFunctionHolder,16>DeferredFinalizeArray;typedefCycleCollectedJSRuntime::DeferredFinalizerTableDeferredFinalizerTable;CycleCollectedJSRuntime*mRuntime;DeferredFinalizeArraymDeferredFinalizeFunctions;uint32_tmFinalizeFunctionToRun;boolmReleasing;staticconstPRTimeSliceMillis=5;/* ms */public:IncrementalFinalizeRunnable(CycleCollectedJSRuntime*aRt,DeferredFinalizerTable&aFinalizerTable);virtual~IncrementalFinalizeRunnable();voidReleaseNow(boolaLimited);NS_DECL_NSIRUNNABLE};}// namespace mozillastructNoteWeakMapChildrenTracer:publicJS::CallbackTracer{NoteWeakMapChildrenTracer(JSRuntime*aRt,nsCycleCollectionNoteRootCallback&aCb):JS::CallbackTracer(aRt),mCb(aCb),mTracedAny(false),mMap(nullptr),mKey(nullptr),mKeyDelegate(nullptr){}voidonChild(constJS::GCCellPtr&aThing)override;nsCycleCollectionNoteRootCallback&mCb;boolmTracedAny;JSObject*mMap;JS::GCCellPtrmKey;JSObject*mKeyDelegate;};voidNoteWeakMapChildrenTracer::onChild(constJS::GCCellPtr&aThing){if(aThing.is<JSString>()){return;}if(!JS::GCThingIsMarkedGray(aThing)&&!mCb.WantAllTraces()){return;}if(AddToCCKind(aThing.kind())){mCb.NoteWeakMapping(mMap,mKey,mKeyDelegate,aThing);mTracedAny=true;}else{JS::TraceChildren(this,aThing);}}structNoteWeakMapsTracer:publicjs::WeakMapTracer{NoteWeakMapsTracer(JSRuntime*aRt,nsCycleCollectionNoteRootCallback&aCccb):js::WeakMapTracer(aRt),mCb(aCccb),mChildTracer(aRt,aCccb){}voidtrace(JSObject*aMap,JS::GCCellPtraKey,JS::GCCellPtraValue)override;nsCycleCollectionNoteRootCallback&mCb;NoteWeakMapChildrenTracermChildTracer;};voidNoteWeakMapsTracer::trace(JSObject*aMap,JS::GCCellPtraKey,JS::GCCellPtraValue){// If nothing that could be held alive by this entry is marked gray, return.if((!aKey||!JS::GCThingIsMarkedGray(aKey))&&MOZ_LIKELY(!mCb.WantAllTraces())){if(!aValue||!JS::GCThingIsMarkedGray(aValue)||aValue.is<JSString>()){return;}}// The cycle collector can only properly reason about weak maps if it can// reason about the liveness of their keys, which in turn requires that// the key can be represented in the cycle collector graph. All existing// uses of weak maps use either objects or scripts as keys, which are okay.MOZ_ASSERT(AddToCCKind(aKey.kind()));// As an emergency fallback for non-debug builds, if the key is not// representable in the cycle collector graph, we treat it as marked. This// can cause leaks, but is preferable to ignoring the binding, which could// cause the cycle collector to free live objects.if(!AddToCCKind(aKey.kind())){aKey=nullptr;}JSObject*kdelegate=nullptr;if(aKey.is<JSObject>()){kdelegate=js::GetWeakmapKeyDelegate(&aKey.as<JSObject>());}if(AddToCCKind(aValue.kind())){mCb.NoteWeakMapping(aMap,aKey,kdelegate,aValue);}else{mChildTracer.mTracedAny=false;mChildTracer.mMap=aMap;mChildTracer.mKey=aKey;mChildTracer.mKeyDelegate=kdelegate;if(!aValue.is<JSString>()){JS::TraceChildren(&mChildTracer,aValue);}// The delegate could hold alive the key, so report something to the CC// if we haven't already.if(!mChildTracer.mTracedAny&&aKey&&JS::GCThingIsMarkedGray(aKey)&&kdelegate){mCb.NoteWeakMapping(aMap,aKey,kdelegate,nullptr);}}}// Report whether the key or value of a weak mapping entry are gray but need to// be marked black.staticvoidShouldWeakMappingEntryBeBlack(JSObject*aMap,JS::GCCellPtraKey,JS::GCCellPtraValue,bool*aKeyShouldBeBlack,bool*aValueShouldBeBlack){*aKeyShouldBeBlack=false;*aValueShouldBeBlack=false;// If nothing that could be held alive by this entry is marked gray, return.boolkeyMightNeedMarking=aKey&&JS::GCThingIsMarkedGray(aKey);boolvalueMightNeedMarking=aValue&&JS::GCThingIsMarkedGray(aValue)&&aValue.kind()!=JS::TraceKind::String;if(!keyMightNeedMarking&&!valueMightNeedMarking){return;}if(!AddToCCKind(aKey.kind())){aKey=nullptr;}if(keyMightNeedMarking&&aKey.is<JSObject>()){JSObject*kdelegate=js::GetWeakmapKeyDelegate(&aKey.as<JSObject>());if(kdelegate&&!JS::ObjectIsMarkedGray(kdelegate)&&(!aMap||!JS::ObjectIsMarkedGray(aMap))){*aKeyShouldBeBlack=true;}}if(aValue&&JS::GCThingIsMarkedGray(aValue)&&(!aKey||!JS::GCThingIsMarkedGray(aKey))&&(!aMap||!JS::ObjectIsMarkedGray(aMap))&&aValue.kind()!=JS::TraceKind::Shape){*aValueShouldBeBlack=true;}}structFixWeakMappingGrayBitsTracer:publicjs::WeakMapTracer{explicitFixWeakMappingGrayBitsTracer(JSRuntime*aRt):js::WeakMapTracer(aRt){}voidFixAll(){do{mAnyMarked=false;js::TraceWeakMaps(this);}while(mAnyMarked);}voidtrace(JSObject*aMap,JS::GCCellPtraKey,JS::GCCellPtraValue)override{boolkeyShouldBeBlack;boolvalueShouldBeBlack;ShouldWeakMappingEntryBeBlack(aMap,aKey,aValue,&keyShouldBeBlack,&valueShouldBeBlack);if(keyShouldBeBlack&&JS::UnmarkGrayGCThingRecursively(aKey)){mAnyMarked=true;}if(valueShouldBeBlack&&JS::UnmarkGrayGCThingRecursively(aValue)){mAnyMarked=true;}}MOZ_INIT_OUTSIDE_CTORboolmAnyMarked;};#ifdef DEBUG// Check whether weak maps are marked correctly according to the logic above.structCheckWeakMappingGrayBitsTracer:publicjs::WeakMapTracer{explicitCheckWeakMappingGrayBitsTracer(JSRuntime*aRt):js::WeakMapTracer(aRt),mFailed(false){}staticboolCheck(JSRuntime*aRt){CheckWeakMappingGrayBitsTracertracer(aRt);js::TraceWeakMaps(&tracer);return!tracer.mFailed;}voidtrace(JSObject*aMap,JS::GCCellPtraKey,JS::GCCellPtraValue)override{boolkeyShouldBeBlack;boolvalueShouldBeBlack;ShouldWeakMappingEntryBeBlack(aMap,aKey,aValue,&keyShouldBeBlack,&valueShouldBeBlack);if(keyShouldBeBlack){fprintf(stderr,"Weak mapping key %p of map %p should be black\n",aKey.asCell(),aMap);mFailed=true;}if(valueShouldBeBlack){fprintf(stderr,"Weak mapping value %p of map %p should be black\n",aValue.asCell(),aMap);mFailed=true;}}boolmFailed;};#endif // DEBUGstaticvoidCheckParticipatesInCycleCollection(JS::GCCellPtraThing,constchar*aName,void*aClosure){bool*cycleCollectionEnabled=static_cast<bool*>(aClosure);if(*cycleCollectionEnabled){return;}if(AddToCCKind(aThing.kind())&&JS::GCThingIsMarkedGray(aThing)){*cycleCollectionEnabled=true;}}NS_IMETHODIMPJSGCThingParticipant::TraverseNative(void*aPtr,nsCycleCollectionTraversalCallback&aCb){autoruntime=reinterpret_cast<CycleCollectedJSRuntime*>(reinterpret_cast<char*>(this)-offsetof(CycleCollectedJSRuntime,mGCThingCycleCollectorGlobal));JS::GCCellPtrcellPtr(aPtr,JS::GCThingTraceKind(aPtr));runtime->TraverseGCThing(CycleCollectedJSRuntime::TRAVERSE_FULL,cellPtr,aCb);returnNS_OK;}// NB: This is only used to initialize the participant in// CycleCollectedJSRuntime. It should never be used directly.staticJSGCThingParticipantsGCThingCycleCollectorGlobal;NS_IMETHODIMPJSZoneParticipant::TraverseNative(void*aPtr,nsCycleCollectionTraversalCallback&aCb){autoruntime=reinterpret_cast<CycleCollectedJSRuntime*>(reinterpret_cast<char*>(this)-offsetof(CycleCollectedJSRuntime,mJSZoneCycleCollectorGlobal));MOZ_ASSERT(!aCb.WantAllTraces());JS::Zone*zone=static_cast<JS::Zone*>(aPtr);runtime->TraverseZone(zone,aCb);returnNS_OK;}structTraversalTracer:publicJS::CallbackTracer{TraversalTracer(JSRuntime*aRt,nsCycleCollectionTraversalCallback&aCb):JS::CallbackTracer(aRt,DoNotTraceWeakMaps),mCb(aCb){}voidonChild(constJS::GCCellPtr&aThing)override;nsCycleCollectionTraversalCallback&mCb;};voidTraversalTracer::onChild(constJS::GCCellPtr&aThing){// Don't traverse non-gray objects, unless we want all traces.if(!JS::GCThingIsMarkedGray(aThing)&&!mCb.WantAllTraces()){return;}/* * This function needs to be careful to avoid stack overflow. Normally, when * AddToCCKind is true, the recursion terminates immediately as we just add * |thing| to the CC graph. So overflow is only possible when there are long * or cyclic chains of non-AddToCCKind GC things. Places where this can occur * use special APIs to handle such chains iteratively. */if(AddToCCKind(aThing.kind())){if(MOZ_UNLIKELY(mCb.WantDebugInfo())){charbuffer[200];getTracingEdgeName(buffer,sizeof(buffer));mCb.NoteNextEdgeName(buffer);}mCb.NoteJSChild(aThing);}elseif(aThing.is<js::Shape>()){// The maximum depth of traversal when tracing a Shape is unbounded, due to// the parent pointers on the shape.JS_TraceShapeCycleCollectorChildren(this,aThing);}elseif(aThing.is<js::ObjectGroup>()){// The maximum depth of traversal when tracing an ObjectGroup is unbounded,// due to information attached to the groups which can lead other groups to// be traced.JS_TraceObjectGroupCycleCollectorChildren(this,aThing);}elseif(!aThing.is<JSString>()){JS::TraceChildren(this,aThing);}}staticvoidNoteJSChildGrayWrapperShim(void*aData,JS::GCCellPtraThing){TraversalTracer*trc=static_cast<TraversalTracer*>(aData);trc->onChild(aThing);}/* * The cycle collection participant for a Zone is intended to produce the same * results as if all of the gray GCthings in a zone were merged into a single node, * except for self-edges. This avoids the overhead of representing all of the GCthings in * the zone in the cycle collector graph, which should be much faster if many of * the GCthings in the zone are gray. * * Zone merging should not always be used, because it is a conservative * approximation of the true cycle collector graph that can incorrectly identify some * garbage objects as being live. For instance, consider two cycles that pass through a * zone, where one is garbage and the other is live. If we merge the entire * zone, the cycle collector will think that both are alive. * * We don't have to worry about losing track of a garbage cycle, because any such garbage * cycle incorrectly identified as live must contain at least one C++ to JS edge, and * XPConnect will always add the C++ object to the CC graph. (This is in contrast to pure * C++ garbage cycles, which must always be properly identified, because we clear the * purple buffer during every CC, which may contain the last reference to a garbage * cycle.) */// NB: This is only used to initialize the participant in// CycleCollectedJSRuntime. It should never be used directly.staticconstJSZoneParticipantsJSZoneCycleCollectorGlobal;staticvoidJSObjectsTenuredCb(JSContext*aContext,void*aData){static_cast<CycleCollectedJSRuntime*>(aData)->JSObjectsTenured();}boolmozilla::GetBuildId(JS::BuildIdCharVector*aBuildID){nsCOMPtr<nsIPlatformInfo>info=do_GetService("@mozilla.org/xre/app-info;1");if(!info){returnfalse;}nsCStringbuildID;nsresultrv=info->GetPlatformBuildID(buildID);NS_ENSURE_SUCCESS(rv,false);if(!aBuildID->resize(buildID.Length())){returnfalse;}for(size_ti=0;i<buildID.Length();i++){(*aBuildID)[i]=buildID[i];}returntrue;}staticvoidMozCrashWarningReporter(JSContext*,JSErrorReport*){MOZ_CRASH("Why is someone touching JSAPI without an AutoJSAPI?");}CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSContext*aCx):mGCThingCycleCollectorGlobal(sGCThingCycleCollectorGlobal),mJSZoneCycleCollectorGlobal(sJSZoneCycleCollectorGlobal),mJSRuntime(JS_GetRuntime(aCx)),mPrevGCSliceCallback(nullptr),mPrevGCNurseryCollectionCallback(nullptr),mJSHolders(256),mOutOfMemoryState(OOMState::OK),mLargeAllocationFailureState(OOMState::OK){MOZ_COUNT_CTOR(CycleCollectedJSRuntime);MOZ_ASSERT(aCx);MOZ_ASSERT(mJSRuntime);if(!JS_AddExtraGCRootsTracer(aCx,TraceBlackJS,this)){MOZ_CRASH("JS_AddExtraGCRootsTracer failed");}JS_SetGrayGCRootsTracer(aCx,TraceGrayJS,this);JS_SetGCCallback(aCx,GCCallback,this);mPrevGCSliceCallback=JS::SetGCSliceCallback(aCx,GCSliceCallback);if(NS_IsMainThread()){// We would like to support all threads here, but the way timeline consumers// are set up currently, you can either add a marker for one specific// docshell, or for every consumer globally. We would like to add a marker// for every consumer observing anything on this thread, but that is not// currently possible. For now, add global markers only when we are on the// main thread, since the UI for this tracing data only displays data// relevant to the main-thread.mPrevGCNurseryCollectionCallback=JS::SetGCNurseryCollectionCallback(aCx,GCNurseryCollectionCallback);}JS_SetObjectsTenuredCallback(aCx,JSObjectsTenuredCb,this);JS::SetOutOfMemoryCallback(aCx,OutOfMemoryCallback,this);JS_SetExternalStringSizeofCallback(aCx,SizeofExternalStringCallback);JS::SetBuildIdOp(aCx,GetBuildId);JS::SetWarningReporter(aCx,MozCrashWarningReporter);#ifdef MOZ_CRASHREPORTERjs::AutoEnterOOMUnsafeRegion::setAnnotateOOMAllocationSizeCallback(CrashReporter::AnnotateOOMAllocationSize);#endifstaticjs::DOMCallbacksDOMcallbacks={InstanceClassHasProtoAtDepth};SetDOMCallbacks(aCx,&DOMcallbacks);js::SetScriptEnvironmentPreparer(aCx,&mEnvironmentPreparer);JS::dbg::SetDebuggerMallocSizeOf(aCx,moz_malloc_size_of);}voidCycleCollectedJSRuntime::Shutdown(JSContext*cx){JS_RemoveExtraGCRootsTracer(cx,TraceBlackJS,this);JS_RemoveExtraGCRootsTracer(cx,TraceGrayJS,this);}CycleCollectedJSRuntime::~CycleCollectedJSRuntime(){MOZ_COUNT_DTOR(CycleCollectedJSRuntime);MOZ_ASSERT(!mDeferredFinalizerTable.Count());}voidCycleCollectedJSRuntime::AddContext(CycleCollectedJSContext*aContext){mContexts.insertBack(aContext);}voidCycleCollectedJSRuntime::RemoveContext(CycleCollectedJSContext*aContext){aContext->removeFrom(mContexts);}size_tCycleCollectedJSRuntime::SizeOfExcludingThis(MallocSizeOfaMallocSizeOf)const{size_tn=0;// We're deliberately not measuring anything hanging off the entries in// mJSHolders.n+=mJSHolders.ShallowSizeOfExcludingThis(aMallocSizeOf);returnn;}voidCycleCollectedJSRuntime::UnmarkSkippableJSHolders(){for(autoiter=mJSHolders.Iter();!iter.Done();iter.Next()){void*holder=iter.Key();nsScriptObjectTracer*&tracer=iter.Data();tracer->CanSkip(holder,true);}}voidCycleCollectedJSRuntime::DescribeGCThing(boolaIsMarked,JS::GCCellPtraThing,nsCycleCollectionTraversalCallback&aCb)const{if(!aCb.WantDebugInfo()){aCb.DescribeGCedNode(aIsMarked,"JS Object");return;}charname[72];uint64_tcompartmentAddress=0;if(aThing.is<JSObject>()){JSObject*obj=&aThing.as<JSObject>();compartmentAddress=(uint64_t)js::GetObjectCompartment(obj);constjs::Class*clasp=js::GetObjectClass(obj);// Give the subclass a chance to do somethingif(DescribeCustomObjects(obj,clasp,name)){// Nothing else to do!}elseif(js::IsFunctionObject(obj)){JSFunction*fun=JS_GetObjectFunction(obj);JSString*str=JS_GetFunctionDisplayId(fun);if(str){JSFlatString*flat=JS_ASSERT_STRING_IS_FLAT(str);nsAutoStringchars;AssignJSFlatString(chars,flat);NS_ConvertUTF16toUTF8fname(chars);SprintfLiteral(name,"JS Object (Function - %s)",fname.get());}else{SprintfLiteral(name,"JS Object (Function)");}}else{SprintfLiteral(name,"JS Object (%s)",clasp->name);}}else{SprintfLiteral(name,"JS %s",JS::GCTraceKindToAscii(aThing.kind()));}// Disable printing global for objects while we figure out ObjShrink fallout.aCb.DescribeGCedNode(aIsMarked,name,compartmentAddress);}voidCycleCollectedJSRuntime::NoteGCThingJSChildren(JS::GCCellPtraThing,nsCycleCollectionTraversalCallback&aCb)const{TraversalTracertrc(mJSRuntime,aCb);JS::TraceChildren(&trc,aThing);}voidCycleCollectedJSRuntime::NoteGCThingXPCOMChildren(constjs::Class*aClasp,JSObject*aObj,nsCycleCollectionTraversalCallback&aCb)const{MOZ_ASSERT(aClasp);MOZ_ASSERT(aClasp==js::GetObjectClass(aObj));if(NoteCustomGCThingXPCOMChildren(aClasp,aObj,aCb)){// Nothing else to do!return;}// XXX This test does seem fragile, we should probably whitelist classes// that do hold a strong reference, but that might not be possible.elseif(aClasp->flags&JSCLASS_HAS_PRIVATE&&aClasp->flags&JSCLASS_PRIVATE_IS_NSISUPPORTS){NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb,"js::GetObjectPrivate(obj)");aCb.NoteXPCOMChild(static_cast<nsISupports*>(js::GetObjectPrivate(aObj)));}else{constDOMJSClass*domClass=GetDOMClass(aObj);if(domClass){NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb,"UnwrapDOMObject(obj)");// It's possible that our object is an unforgeable holder object, in// which case it doesn't actually have a C++ DOM object associated with// it. Use UnwrapPossiblyNotInitializedDOMObject, which produces null in// that case, since NoteXPCOMChild/NoteNativeChild are null-safe.if(domClass->mDOMObjectIsISupports){aCb.NoteXPCOMChild(UnwrapPossiblyNotInitializedDOMObject<nsISupports>(aObj));}elseif(domClass->mParticipant){aCb.NoteNativeChild(UnwrapPossiblyNotInitializedDOMObject<void>(aObj),domClass->mParticipant);}}}}voidCycleCollectedJSRuntime::TraverseGCThing(TraverseSelectaTs,JS::GCCellPtraThing,nsCycleCollectionTraversalCallback&aCb){boolisMarkedGray=JS::GCThingIsMarkedGray(aThing);if(aTs==TRAVERSE_FULL){DescribeGCThing(!isMarkedGray,aThing,aCb);}// If this object is alive, then all of its children are alive. For JS objects,// the black-gray invariant ensures the children are also marked black. For C++// objects, the ref count from this object will keep them alive. Thus we don't// need to trace our children, unless we are debugging using WantAllTraces.if(!isMarkedGray&&!aCb.WantAllTraces()){return;}if(aTs==TRAVERSE_FULL){NoteGCThingJSChildren(aThing,aCb);}if(aThing.is<JSObject>()){JSObject*obj=&aThing.as<JSObject>();NoteGCThingXPCOMChildren(js::GetObjectClass(obj),obj,aCb);}}structTraverseObjectShimClosure{nsCycleCollectionTraversalCallback&cb;CycleCollectedJSRuntime*self;};voidCycleCollectedJSRuntime::TraverseZone(JS::Zone*aZone,nsCycleCollectionTraversalCallback&aCb){/* * We treat the zone as being gray. We handle non-gray GCthings in the * zone by not reporting their children to the CC. The black-gray invariant * ensures that any JS children will also be non-gray, and thus don't need to be * added to the graph. For C++ children, not representing the edge from the * non-gray JS GCthings to the C++ object will keep the child alive. * * We don't allow zone merging in a WantAllTraces CC, because then these * assumptions don't hold. */aCb.DescribeGCedNode(false,"JS Zone");/* * Every JS child of everything in the zone is either in the zone * or is a cross-compartment wrapper. In the former case, we don't need to * represent these edges in the CC graph because JS objects are not ref counted. * In the latter case, the JS engine keeps a map of these wrappers, which we * iterate over. Edges between compartments in the same zone will add * unnecessary loop edges to the graph (bug 842137). */TraversalTracertrc(mJSRuntime,aCb);js::VisitGrayWrapperTargets(aZone,NoteJSChildGrayWrapperShim,&trc);/* * To find C++ children of things in the zone, we scan every JS Object in * the zone. Only JS Objects can have C++ children. */TraverseObjectShimClosureclosure={aCb,this};js::IterateGrayObjects(aZone,TraverseObjectShim,&closure);}/* static */voidCycleCollectedJSRuntime::TraverseObjectShim(void*aData,JS::GCCellPtraThing){TraverseObjectShimClosure*closure=static_cast<TraverseObjectShimClosure*>(aData);MOZ_ASSERT(aThing.is<JSObject>());closure->self->TraverseGCThing(CycleCollectedJSRuntime::TRAVERSE_CPP,aThing,closure->cb);}voidCycleCollectedJSRuntime::TraverseNativeRoots(nsCycleCollectionNoteRootCallback&aCb){// NB: This is here just to preserve the existing XPConnect order. I doubt it// would hurt to do this after the JS holders.TraverseAdditionalNativeRoots(aCb);for(autoiter=mJSHolders.Iter();!iter.Done();iter.Next()){void*holder=iter.Key();nsScriptObjectTracer*&tracer=iter.Data();boolnoteRoot=false;if(MOZ_UNLIKELY(aCb.WantAllTraces())){noteRoot=true;}else{tracer->Trace(holder,TraceCallbackFunc(CheckParticipatesInCycleCollection),¬eRoot);}if(noteRoot){aCb.NoteNativeRoot(holder,tracer);}}}/* static */voidCycleCollectedJSRuntime::TraceBlackJS(JSTracer*aTracer,void*aData){CycleCollectedJSRuntime*self=static_cast<CycleCollectedJSRuntime*>(aData);self->TraceNativeBlackRoots(aTracer);}/* static */voidCycleCollectedJSRuntime::TraceGrayJS(JSTracer*aTracer,void*aData){CycleCollectedJSRuntime*self=static_cast<CycleCollectedJSRuntime*>(aData);// Mark these roots as gray so the CC can walk them later.self->TraceNativeGrayRoots(aTracer);}/* static */voidCycleCollectedJSRuntime::GCCallback(JSContext*aContext,JSGCStatusaStatus,void*aData){CycleCollectedJSRuntime*self=static_cast<CycleCollectedJSRuntime*>(aData);MOZ_ASSERT(CycleCollectedJSContext::Get()->Context()==aContext);MOZ_ASSERT(CycleCollectedJSContext::Get()->Runtime()==self);self->OnGC(aContext,aStatus);}/* static */voidCycleCollectedJSRuntime::GCSliceCallback(JSContext*aContext,JS::GCProgressaProgress,constJS::GCDescription&aDesc){CycleCollectedJSRuntime*self=CycleCollectedJSRuntime::Get();MOZ_ASSERT(CycleCollectedJSContext::Get()->Context()==aContext);#ifdef MOZ_GECKO_PROFILERif(profiler_is_active()){if(aProgress==JS::GC_CYCLE_END){profiler_add_marker("GCMajor",MakeUnique<GCMajorMarkerPayload>(aDesc.startTime(aContext),aDesc.endTime(aContext),aDesc.summaryToJSON(aContext)));}elseif(aProgress==JS::GC_SLICE_END){profiler_add_marker("GCSlice",MakeUnique<GCSliceMarkerPayload>(aDesc.lastSliceStart(aContext),aDesc.lastSliceEnd(aContext),aDesc.sliceToJSON(aContext)));}}#endifif(aProgress==JS::GC_CYCLE_END){JS::gcreason::Reasonreason=aDesc.reason_;Unused<<NS_WARN_IF(NS_FAILED(DebuggerOnGCRunnable::Enqueue(aContext,aDesc))&&reason!=JS::gcreason::SHUTDOWN_CC&&reason!=JS::gcreason::DESTROY_RUNTIME&&reason!=JS::gcreason::XPCONNECT_SHUTDOWN);}if(self->mPrevGCSliceCallback){self->mPrevGCSliceCallback(aContext,aProgress,aDesc);}}classMinorGCMarker:publicTimelineMarker{private:JS::gcreason::ReasonmReason;public:MinorGCMarker(MarkerTracingTypeaTracingType,JS::gcreason::ReasonaReason):TimelineMarker("MinorGC",aTracingType,MarkerStackRequest::NO_STACK),mReason(aReason){MOZ_ASSERT(aTracingType==MarkerTracingType::START||aTracingType==MarkerTracingType::END);}MinorGCMarker(JS::GCNurseryProgressaProgress,JS::gcreason::ReasonaReason):TimelineMarker("MinorGC",aProgress==JS::GCNurseryProgress::GC_NURSERY_COLLECTION_START?MarkerTracingType::START:MarkerTracingType::END,MarkerStackRequest::NO_STACK),mReason(aReason){}virtualvoidAddDetails(JSContext*aCx,dom::ProfileTimelineMarker&aMarker)override{TimelineMarker::AddDetails(aCx,aMarker);if(GetTracingType()==MarkerTracingType::START){autoreason=JS::gcreason::ExplainReason(mReason);aMarker.mCauseName.Construct(NS_ConvertUTF8toUTF16(reason));}}virtualUniquePtr<AbstractTimelineMarker>Clone()override{autoclone=MakeUnique<MinorGCMarker>(GetTracingType(),mReason);clone->SetCustomTime(GetTime());returnUniquePtr<AbstractTimelineMarker>(Move(clone));}};/* static */voidCycleCollectedJSRuntime::GCNurseryCollectionCallback(JSContext*aContext,JS::GCNurseryProgressaProgress,JS::gcreason::ReasonaReason){CycleCollectedJSRuntime*self=CycleCollectedJSRuntime::Get();MOZ_ASSERT(CycleCollectedJSContext::Get()->Context()==aContext);MOZ_ASSERT(NS_IsMainThread());RefPtr<TimelineConsumers>timelines=TimelineConsumers::Get();if(timelines&&!timelines->IsEmpty()){UniquePtr<AbstractTimelineMarker>abstractMarker(MakeUnique<MinorGCMarker>(aProgress,aReason));timelines->AddMarkerForAllObservedDocShells(abstractMarker);}if(aProgress==JS::GCNurseryProgress::GC_NURSERY_COLLECTION_START){self->mLatestNurseryCollectionStart=TimeStamp::Now();}elseif((aProgress==JS::GCNurseryProgress::GC_NURSERY_COLLECTION_END)&&profiler_is_active()){#ifdef MOZ_GECKO_PROFILERprofiler_add_marker("GCMinor",MakeUnique<GCMinorMarkerPayload>(self->mLatestNurseryCollectionStart,TimeStamp::Now(),JS::MinorGcToJSON(aContext)));#endif}if(self->mPrevGCNurseryCollectionCallback){self->mPrevGCNurseryCollectionCallback(aContext,aProgress,aReason);}}/* static */voidCycleCollectedJSRuntime::OutOfMemoryCallback(JSContext*aContext,void*aData){CycleCollectedJSRuntime*self=static_cast<CycleCollectedJSRuntime*>(aData);MOZ_ASSERT(CycleCollectedJSContext::Get()->Context()==aContext);MOZ_ASSERT(CycleCollectedJSContext::Get()->Runtime()==self);self->OnOutOfMemory();}/* static */size_tCycleCollectedJSRuntime::SizeofExternalStringCallback(JSString*aStr,MallocSizeOfaMallocSizeOf){// We promised the JS engine we would not GC. Enforce that:JS::AutoCheckCannotGCautoCannotGC;if(!XPCStringConvert::IsDOMString(aStr)){// Might be a literal or something we don't understand. Just claim 0.return0;}constchar16_t*chars=JS_GetTwoByteExternalStringChars(aStr);constnsStringBuffer*buf=nsStringBuffer::FromData((void*)chars);// We want sizeof including this, because the entire string buffer is owned by// the external string. But only report here if we're unshared; if we're// shared then we don't know who really owns this data.returnbuf->SizeOfIncludingThisIfUnshared(aMallocSizeOf);}structJsGcTracer:publicTraceCallbacks{virtualvoidTrace(JS::Heap<JS::Value>*aPtr,constchar*aName,void*aClosure)constoverride{JS::TraceEdge(static_cast<JSTracer*>(aClosure),aPtr,aName);}virtualvoidTrace(JS::Heap<jsid>*aPtr,constchar*aName,void*aClosure)constoverride{JS::TraceEdge(static_cast<JSTracer*>(aClosure),aPtr,aName);}virtualvoidTrace(JS::Heap<JSObject*>*aPtr,constchar*aName,void*aClosure)constoverride{JS::TraceEdge(static_cast<JSTracer*>(aClosure),aPtr,aName);}virtualvoidTrace(JSObject**aPtr,constchar*aName,void*aClosure)constoverride{js::UnsafeTraceManuallyBarrieredEdge(static_cast<JSTracer*>(aClosure),aPtr,aName);}virtualvoidTrace(JS::TenuredHeap<JSObject*>*aPtr,constchar*aName,void*aClosure)constoverride{JS::TraceEdge(static_cast<JSTracer*>(aClosure),aPtr,aName);}virtualvoidTrace(JS::Heap<JSString*>*aPtr,constchar*aName,void*aClosure)constoverride{JS::TraceEdge(static_cast<JSTracer*>(aClosure),aPtr,aName);}virtualvoidTrace(JS::Heap<JSScript*>*aPtr,constchar*aName,void*aClosure)constoverride{JS::TraceEdge(static_cast<JSTracer*>(aClosure),aPtr,aName);}virtualvoidTrace(JS::Heap<JSFunction*>*aPtr,constchar*aName,void*aClosure)constoverride{JS::TraceEdge(static_cast<JSTracer*>(aClosure),aPtr,aName);}};voidmozilla::TraceScriptHolder(nsISupports*aHolder,JSTracer*aTracer){nsXPCOMCycleCollectionParticipant*participant=nullptr;CallQueryInterface(aHolder,&participant);participant->Trace(aHolder,JsGcTracer(),aTracer);}voidCycleCollectedJSRuntime::TraceNativeGrayRoots(JSTracer*aTracer){// NB: This is here just to preserve the existing XPConnect order. I doubt it// would hurt to do this after the JS holders.TraceAdditionalNativeGrayRoots(aTracer);for(autoiter=mJSHolders.Iter();!iter.Done();iter.Next()){void*holder=iter.Key();nsScriptObjectTracer*&tracer=iter.Data();tracer->Trace(holder,JsGcTracer(),aTracer);}}voidCycleCollectedJSRuntime::AddJSHolder(void*aHolder,nsScriptObjectTracer*aTracer){mJSHolders.Put(aHolder,aTracer);}structClearJSHolder:publicTraceCallbacks{virtualvoidTrace(JS::Heap<JS::Value>*aPtr,constchar*,void*)constoverride{aPtr->setUndefined();}virtualvoidTrace(JS::Heap<jsid>*aPtr,constchar*,void*)constoverride{*aPtr=JSID_VOID;}virtualvoidTrace(JS::Heap<JSObject*>*aPtr,constchar*,void*)constoverride{*aPtr=nullptr;}virtualvoidTrace(JSObject**aPtr,constchar*aName,void*aClosure)constoverride{*aPtr=nullptr;}virtualvoidTrace(JS::TenuredHeap<JSObject*>*aPtr,constchar*,void*)constoverride{*aPtr=nullptr;}virtualvoidTrace(JS::Heap<JSString*>*aPtr,constchar*,void*)constoverride{*aPtr=nullptr;}virtualvoidTrace(JS::Heap<JSScript*>*aPtr,constchar*,void*)constoverride{*aPtr=nullptr;}virtualvoidTrace(JS::Heap<JSFunction*>*aPtr,constchar*,void*)constoverride{*aPtr=nullptr;}};voidCycleCollectedJSRuntime::RemoveJSHolder(void*aHolder){if(autoentry=mJSHolders.Lookup(aHolder)){entry.Data()->Trace(aHolder,ClearJSHolder(),nullptr);entry.Remove();}}#ifdef DEBUGboolCycleCollectedJSRuntime::IsJSHolder(void*aHolder){returnmJSHolders.Get(aHolder,nullptr);}staticvoidAssertNoGcThing(JS::GCCellPtraGCThing,constchar*aName,void*aClosure){MOZ_ASSERT(!aGCThing);}voidCycleCollectedJSRuntime::AssertNoObjectsToTrace(void*aPossibleJSHolder){nsScriptObjectTracer*tracer=mJSHolders.Get(aPossibleJSHolder);if(tracer){tracer->Trace(aPossibleJSHolder,TraceCallbackFunc(AssertNoGcThing),nullptr);}}#endifnsCycleCollectionParticipant*CycleCollectedJSRuntime::GCThingParticipant(){return&mGCThingCycleCollectorGlobal;}nsCycleCollectionParticipant*CycleCollectedJSRuntime::ZoneParticipant(){return&mJSZoneCycleCollectorGlobal;}nsresultCycleCollectedJSRuntime::TraverseRoots(nsCycleCollectionNoteRootCallback&aCb){TraverseNativeRoots(aCb);NoteWeakMapsTracertrc(mJSRuntime,aCb);js::TraceWeakMaps(&trc);returnNS_OK;}boolCycleCollectedJSRuntime::UsefulToMergeZones()const{returnfalse;}voidCycleCollectedJSRuntime::FixWeakMappingGrayBits()const{MOZ_ASSERT(!JS::IsIncrementalGCInProgress(mJSRuntime),"Don't call FixWeakMappingGrayBits during a GC.");FixWeakMappingGrayBitsTracerfixer(mJSRuntime);fixer.FixAll();}voidCycleCollectedJSRuntime::CheckGrayBits()const{MOZ_ASSERT(!JS::IsIncrementalGCInProgress(mJSRuntime),"Don't call CheckGrayBits during a GC.");#ifndef ANDROID// Bug 1346874 - The gray state check is expensive. Android tests are already// slow enough that this check can easily push them over the threshold to a// timeout.MOZ_ASSERT(js::CheckGrayMarkingState(mJSRuntime));MOZ_ASSERT(CheckWeakMappingGrayBitsTracer::Check(mJSRuntime));#endif}boolCycleCollectedJSRuntime::AreGCGrayBitsValid()const{returnjs::AreGCGrayBitsValid(mJSRuntime);}voidCycleCollectedJSRuntime::GarbageCollect(uint32_taReason)const{MOZ_ASSERT(aReason<JS::gcreason::NUM_REASONS);JS::gcreason::Reasongcreason=static_cast<JS::gcreason::Reason>(aReason);JSContext*cx=CycleCollectedJSContext::Get()->Context();JS::PrepareForFullGC(cx);JS::GCForReason(cx,GC_NORMAL,gcreason);}voidCycleCollectedJSRuntime::JSObjectsTenured(){for(autoiter=mNurseryObjects.Iter();!iter.Done();iter.Next()){nsWrapperCache*cache=iter.Get();JSObject*wrapper=cache->GetWrapperMaybeDead();MOZ_DIAGNOSTIC_ASSERT(wrapper);if(!JS::ObjectIsTenured(wrapper)){MOZ_ASSERT(!cache->PreservingWrapper());constJSClass*jsClass=js::GetObjectJSClass(wrapper);jsClass->doFinalize(nullptr,wrapper);}}#ifdef DEBUGfor(autoiter=mPreservedNurseryObjects.Iter();!iter.Done();iter.Next()){MOZ_ASSERT(JS::ObjectIsTenured(iter.Get().get()));}#endifmNurseryObjects.Clear();mPreservedNurseryObjects.Clear();}voidCycleCollectedJSRuntime::NurseryWrapperAdded(nsWrapperCache*aCache){MOZ_ASSERT(aCache);MOZ_ASSERT(aCache->GetWrapperMaybeDead());MOZ_ASSERT(!JS::ObjectIsTenured(aCache->GetWrapperMaybeDead()));mNurseryObjects.InfallibleAppend(aCache);}voidCycleCollectedJSRuntime::NurseryWrapperPreserved(JSObject*aWrapper){mPreservedNurseryObjects.InfallibleAppend(JS::PersistentRooted<JSObject*>(mJSRuntime,aWrapper));}voidCycleCollectedJSRuntime::DeferredFinalize(DeferredFinalizeAppendFunctionaAppendFunc,DeferredFinalizeFunctionaFunc,void*aThing){if(autoentry=mDeferredFinalizerTable.LookupForAdd(aFunc)){aAppendFunc(entry.Data(),aThing);}else{entry.OrInsert([aAppendFunc,aThing](){returnaAppendFunc(nullptr,aThing);});}}voidCycleCollectedJSRuntime::DeferredFinalize(nsISupports*aSupports){typedefDeferredFinalizerImpl<nsISupports>Impl;DeferredFinalize(Impl::AppendDeferredFinalizePointer,Impl::DeferredFinalize,aSupports);}voidCycleCollectedJSRuntime::DumpJSHeap(FILE*aFile){JSContext*cx=CycleCollectedJSContext::Get()->Context();js::DumpHeap(cx,aFile,js::CollectNurseryBeforeDump);}IncrementalFinalizeRunnable::IncrementalFinalizeRunnable(CycleCollectedJSRuntime*aRt,DeferredFinalizerTable&aFinalizers):Runnable("IncrementalFinalizeRunnable"),mRuntime(aRt),mFinalizeFunctionToRun(0),mReleasing(false){for(autoiter=aFinalizers.Iter();!iter.Done();iter.Next()){DeferredFinalizeFunction&function=iter.Key();void*&data=iter.Data();DeferredFinalizeFunctionHolder*holder=mDeferredFinalizeFunctions.AppendElement();holder->run=function;holder->data=data;iter.Remove();}}IncrementalFinalizeRunnable::~IncrementalFinalizeRunnable(){MOZ_ASSERT(this!=mRuntime->mFinalizeRunnable);}voidIncrementalFinalizeRunnable::ReleaseNow(boolaLimited){if(mReleasing){NS_WARNING("Re-entering ReleaseNow");return;}{mozilla::AutoRestore<bool>ar(mReleasing);mReleasing=true;MOZ_ASSERT(mDeferredFinalizeFunctions.Length()!=0,"We should have at least ReleaseSliceNow to run");MOZ_ASSERT(mFinalizeFunctionToRun<mDeferredFinalizeFunctions.Length(),"No more finalizers to run?");TimeDurationsliceTime=TimeDuration::FromMilliseconds(SliceMillis);TimeStampstarted=TimeStamp::Now();booltimeout=false;do{constDeferredFinalizeFunctionHolder&function=mDeferredFinalizeFunctions[mFinalizeFunctionToRun];if(aLimited){booldone=false;while(!timeout&&!done){/* * We don't want to read the clock too often, so we try to * release slices of 100 items. */done=function.run(100,function.data);timeout=TimeStamp::Now()-started>=sliceTime;}if(done){++mFinalizeFunctionToRun;}if(timeout){break;}}else{while(!function.run(UINT32_MAX,function.data));++mFinalizeFunctionToRun;}}while(mFinalizeFunctionToRun<mDeferredFinalizeFunctions.Length());}if(mFinalizeFunctionToRun==mDeferredFinalizeFunctions.Length()){MOZ_ASSERT(mRuntime->mFinalizeRunnable==this);mDeferredFinalizeFunctions.Clear();// NB: This may delete this!mRuntime->mFinalizeRunnable=nullptr;}}NS_IMETHODIMPIncrementalFinalizeRunnable::Run(){if(mRuntime->mFinalizeRunnable!=this){/* These items were already processed synchronously in JSGC_END. */MOZ_ASSERT(!mDeferredFinalizeFunctions.Length());returnNS_OK;}TimeStampstart=TimeStamp::Now();ReleaseNow(true);if(mDeferredFinalizeFunctions.Length()){nsresultrv=NS_DispatchToCurrentThread(this);if(NS_FAILED(rv)){ReleaseNow(false);}}uint32_tduration=(uint32_t)((TimeStamp::Now()-start).ToMilliseconds());Telemetry::Accumulate(Telemetry::DEFERRED_FINALIZE_ASYNC,duration);returnNS_OK;}voidCycleCollectedJSRuntime::FinalizeDeferredThings(CycleCollectedJSContext::DeferredFinalizeTypeaType){/* * If the previous GC created a runnable to finalize objects * incrementally, and if it hasn't finished yet, finish it now. We * don't want these to build up. We also don't want to allow any * existing incremental finalize runnables to run after a * non-incremental GC, since they are often used to detect leaks. */if(mFinalizeRunnable){mFinalizeRunnable->ReleaseNow(false);if(mFinalizeRunnable){// If we re-entered ReleaseNow, we couldn't delete mFinalizeRunnable and// we need to just continue processing it.return;}}if(mDeferredFinalizerTable.Count()==0){return;}mFinalizeRunnable=newIncrementalFinalizeRunnable(this,mDeferredFinalizerTable);// Everything should be gone now.MOZ_ASSERT(mDeferredFinalizerTable.Count()==0);if(aType==CycleCollectedJSContext::FinalizeIncrementally){NS_DispatchToCurrentThread(mFinalizeRunnable);}else{mFinalizeRunnable->ReleaseNow(false);MOZ_ASSERT(!mFinalizeRunnable);}}voidCycleCollectedJSRuntime::AnnotateAndSetOutOfMemory(OOMState*aStatePtr,OOMStateaNewState){*aStatePtr=aNewState;#ifdef MOZ_CRASHREPORTERCrashReporter::AnnotateCrashReport(aStatePtr==&mOutOfMemoryState?NS_LITERAL_CSTRING("JSOutOfMemory"):NS_LITERAL_CSTRING("JSLargeAllocationFailure"),aNewState==OOMState::Reporting?NS_LITERAL_CSTRING("Reporting"):aNewState==OOMState::Reported?NS_LITERAL_CSTRING("Reported"):NS_LITERAL_CSTRING("Recovered"));#endif}voidCycleCollectedJSRuntime::OnGC(JSContext*aContext,JSGCStatusaStatus){switch(aStatus){caseJSGC_BEGIN:nsCycleCollector_prepareForGarbageCollection();mZonesWaitingForGC.Clear();break;caseJSGC_END:{#ifdef MOZ_CRASHREPORTERif(mOutOfMemoryState==OOMState::Reported){AnnotateAndSetOutOfMemory(&mOutOfMemoryState,OOMState::Recovered);}if(mLargeAllocationFailureState==OOMState::Reported){AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState,OOMState::Recovered);}#endif// Do any deferred finalization of native objects. Normally we do this// incrementally for an incremental GC, and immediately for a// non-incremental GC, on the basis that the type of GC reflects how// urgently resources should be destroyed. However under some circumstances// (such as in js::InternalCallOrConstruct) we can end up running a// non-incremental GC when there is a pending exception, and the finalizers// are not set up to handle that. In that case, just run them later, after// we've returned to the event loop.boolfinalizeIncrementally=JS::WasIncrementalGC(mJSRuntime)||JS_IsExceptionPending(aContext);FinalizeDeferredThings(finalizeIncrementally?CycleCollectedJSContext::FinalizeIncrementally:CycleCollectedJSContext::FinalizeNow);break;}default:MOZ_CRASH();}CustomGCCallback(aStatus);}voidCycleCollectedJSRuntime::OnOutOfMemory(){AnnotateAndSetOutOfMemory(&mOutOfMemoryState,OOMState::Reporting);CustomOutOfMemoryCallback();AnnotateAndSetOutOfMemory(&mOutOfMemoryState,OOMState::Reported);}voidCycleCollectedJSRuntime::SetLargeAllocationFailure(OOMStateaNewState){AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState,aNewState);}voidCycleCollectedJSRuntime::PrepareWaitingZonesForGC(){JSContext*cx=CycleCollectedJSContext::Get()->Context();if(mZonesWaitingForGC.Count()==0){JS::PrepareForFullGC(cx);}else{for(autoiter=mZonesWaitingForGC.Iter();!iter.Done();iter.Next()){JS::PrepareZoneForGC(iter.Get()->GetKey());}mZonesWaitingForGC.Clear();}}voidCycleCollectedJSRuntime::EnvironmentPreparer::invoke(JS::HandleObjectscope,js::ScriptEnvironmentPreparer::Closure&closure){nsIGlobalObject*global=xpc::NativeGlobal(scope);// Not much we can do if we simply don't have a usable global here...NS_ENSURE_TRUE_VOID(global&&global->GetGlobalJSObject());AutoEntryScriptaes(global,"JS-engine-initiated execution");MOZ_ASSERT(!JS_IsExceptionPending(aes.cx()));DebugOnly<bool>ok=closure(aes.cx());MOZ_ASSERT_IF(ok,!JS_IsExceptionPending(aes.cx()));// The AutoEntryScript will check for JS_IsExceptionPending on the// JSContext and report it as needed as it comes off the stack.}/* static */CycleCollectedJSRuntime*CycleCollectedJSRuntime::Get(){autocontext=CycleCollectedJSContext::Get();if(context){returncontext->Runtime();}returnnullptr;}